{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "77gENRVX40S7"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "d8jyt37T42Vf"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "aPxHdjwW5P2j"
      },
      "outputs": [],
      "source": [
        "#@title MIT License\n",
        "#\n",
        "# Copyright (c) 2017 François Chollet                                                                                                                    # IGNORE_COPYRIGHT: cleared by OSS licensing\n",
        "#\n",
        "# Permission is hereby granted, free of charge, to any person obtaining a\n",
        "# copy of this software and associated documentation files (the \"Software\"),\n",
        "# to deal in the Software without restriction, including without limitation\n",
        "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
        "# and/or sell copies of the Software, and to permit persons to whom the\n",
        "# Software is furnished to do so, subject to the following conditions:\n",
        "#\n",
        "# The above copyright notice and this permission notice shall be included in\n",
        "# all copies or substantial portions of the Software.\n",
        "#\n",
        "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
        "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
        "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
        "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
        "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
        "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
        "# DEALINGS IN THE SOFTWARE."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hRTa3Ee15WsJ"
      },
      "source": [
        "# Transferência de Aprendizado com uma ConvNet Pré-Treinada"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dQHMcypT3vDT"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/images/transfer_learning\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Ver em TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/pt-br/tutorials/images/transfer_learning.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Executar no Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/pt-br/tutorials/images/transfer_learning.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Visualizar Código Fonte no GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/pt-br/tutorials/images/transfer_learning.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Baixar notebook</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2X4KyhORdSeO"
      },
      "source": [
        "Neste tutorial, você aprenderá a classificar imagens de cães e gatos usando a transferência de aprendizado de uma rede pré-treinada.\n",
        "\n",
        "Um modelo pré-treinado é uma rede salva que foi treinada anteriormente em um grande conjunto de dados, geralmente em uma tarefa de classificação de imagem em larga escala. Você usa o modelo pré-treinado como está ou usa a transferência de aprendizado para personalizar esse modelo para uma determinada tarefa.\n",
        "\n",
        "A intuição por trás da transferência de aprendizado para classificação de imagens é que, se um modelo for treinado em um conjunto de dados grande e geral o suficiente, esse modelo servirá efetivamente como um modelo genérico do mundo visual. Você pode aproveitar esses mapas de características aprendidas sem precisar começar do zero treinando um modelo grande em um grande conjunto de dados.\n",
        "\n",
        "Neste notebook, você tentará duas maneiras de personalizar um modelo pré-treinado:\n",
        "\n",
        "1. Extração de características: use as representações aprendidas por uma rede anterior para extrair características significativas de novas amostras. Você simplesmente adiciona um novo classificador, que será treinado do zero, sobre o modelo pré-treinado, para que você possa adaptar novamente os mapas de características aprendidas anteriormente para o conjunto de dados.\n",
        "\n",
        " Você não precisa (re) treinar o modelo inteiro. A rede convolucional de base já contém características que são genericamente úteis para classificar imagens. No entanto, a parte final de classificação do modelo pré-treinado é específica para a tarefa de classificação original e subsequentemente específica para o conjunto de classes em que o modelo foi treinado.\n",
        "\n",
        "1. Ajuste fino: descongele algumas das camadas superiores de uma base modelo congelada e treine em conjunto as camadas de classificação recém-adicionadas e as últimas camadas do modelo base. Isso nos permite \"ajustar\" as representações de características de ordem superior no modelo base para torná-las mais relevantes para a tarefa específica.\n",
        "\n",
        "Você seguirá o fluxo de trabalho geral de aprendizado de máquina.\n",
        "\n",
        "1. Examine e entenda os dados\n",
        "1. Crie um pipeline de entrada, neste caso usando o Keras ImageDataGenerator\n",
        "1. Componha o modelo\n",
        "   * Carregar no modelo básico pré-treinado (e pesos pré-treinados)\n",
        "   * Empilhe as camadas de classificação na parte superior\n",
        "1. Treine o modelo\n",
        "1. Avalie o modelo\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "iBMcobPHdD8O"
      },
      "outputs": [],
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "import os\n",
        "\n",
        "import numpy as np\n",
        "\n",
        "import matplotlib.pyplot as plt"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "TqOt6Sv7AsMi"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "  # %tensorflow_version only exists in Colab.\n",
        "  %tensorflow_version 2.x\n",
        "except Exception:\n",
        "  pass\n",
        "import tensorflow as tf\n",
        "\n",
        "keras = tf.keras"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "v77rlkCKW0IJ"
      },
      "source": [
        "## Pré-Processamento dos Dados"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0GoKGm1duzgk"
      },
      "source": [
        "### Baixar os Dados"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vHP9qMJxt2oz"
      },
      "source": [
        "Use [Conjuntos de dados TensorFlow] (http://tensorflow.org/datasets) para carregar o conjunto de dados de cães e gatos.\n",
        "\n",
        "O pacote `tfds` é a maneira mais fácil de carregar dados pré-definidos. Se você possui seus próprios dados e está interessado em importá-los com o TensorFlow, consulte [carregando dados da imagem] (../load_data/images.ipynb)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "KVh7rDVAuW8Y"
      },
      "outputs": [],
      "source": [
        "import tensorflow_datasets as tfds\n",
        "tfds.disable_progress_bar()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Nsoic6bGuwQ-"
      },
      "source": [
        "O método `tfds.load` baixa e armazena em cache os dados e retorna um objeto `tf.data.Dataset`. Esses objetos fornecem métodos poderosos e eficientes para manipular dados e canalizá-los para o seu modelo.\n",
        "\n",
        "Como `\"cats_vs_dogs\"` não define padrões para divisões, use o recurso subsplit para dividi-lo em (treinamento, validação, teste) com 80%, 10% e 10% dos dados, respectivamente."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ro4oYaEmxe4r"
      },
      "outputs": [],
      "source": [
        "SPLIT_WEIGHTS = (8, 1, 1)\n",
        "splits = tfds.Split.TRAIN.subsplit(weighted=SPLIT_WEIGHTS)\n",
        "\n",
        "(raw_train, raw_validation, raw_test), metadata = tfds.load(\n",
        "    'cats_vs_dogs', split=list(splits),\n",
        "    with_info=True, as_supervised=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "o29EfE-p0g5X"
      },
      "source": [
        "Os objetos `tf.data.Dataset` resultantes contêm pares `(image, label)` onde as imagens têm formato variável e 3 canais, e o rótulo é escalar."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GIys1_zY1S9b"
      },
      "outputs": [],
      "source": [
        "print(raw_train)\n",
        "print(raw_validation)\n",
        "print(raw_test)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yO1Q2JaW5sIy"
      },
      "source": [
        "Mostre as duas primeiras imagens e rótulos do conjunto de treinamento:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "K5BeQyKThC_Y"
      },
      "outputs": [],
      "source": [
        "get_label_name = metadata.features['label'].int2str\n",
        "\n",
        "for image, label in raw_train.take(2):\n",
        "  plt.figure()\n",
        "  plt.imshow(image)\n",
        "  plt.title(get_label_name(label))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wvidPx6jeFzf"
      },
      "source": [
        "### Formate os dados\n",
        "\n",
        "Use o módulo `tf.image` para formatar as imagens para a tarefa.\n",
        "\n",
        "Redimensione as imagens para um tamanho de entrada fixo e redimensione os canais de entrada para um intervalo de `[-1,1]`\n",
        "\n",
        "<!-- TODO (markdaoust): corrige as funções de pré-processamento keras_applications para trabalhar em tf2 -->"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "y3PM6GVHcC31"
      },
      "outputs": [],
      "source": [
        "IMG_SIZE = 160 # Todas as imagens serão ajustadas para 160x160\n",
        "\n",
        "def format_example(image, label):\n",
        "  image = tf.cast(image, tf.float32)\n",
        "  image = (image/127.5) - 1\n",
        "  image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE))\n",
        "  return image, label"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "i2MRh_AeBtOM"
      },
      "source": [
        "Aplique esta função a cada item no conjunto de dados usando o método map:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "SFZ6ZW7KSXP9"
      },
      "outputs": [],
      "source": [
        "train = raw_train.map(format_example)\n",
        "validation = raw_validation.map(format_example)\n",
        "test = raw_test.map(format_example)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "E5ifgXDuBfOC"
      },
      "source": [
        "Agora embaralhe e agrupe os dados."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Yic-I66m6Isv"
      },
      "outputs": [],
      "source": [
        "BATCH_SIZE = 32\n",
        "SHUFFLE_BUFFER_SIZE = 1000"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "p3UUPdm86LNC"
      },
      "outputs": [],
      "source": [
        "train_batches = train.shuffle(SHUFFLE_BUFFER_SIZE).batch(BATCH_SIZE)\n",
        "validation_batches = validation.batch(BATCH_SIZE)\n",
        "test_batches = test.batch(BATCH_SIZE)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "02rJpcFtChP0"
      },
      "source": [
        "Inspecione um lote de dados:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "iknFo3ELBVho"
      },
      "outputs": [],
      "source": [
        "for image_batch, label_batch in train_batches.take(1):\n",
        "   pass\n",
        "\n",
        "image_batch.shape"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OkH-kazQecHB"
      },
      "source": [
        "## Crie o modelo base a partir das ConvNets pré-treinadas\n",
        "Você criará o modelo base a partir do modelo **MobileNet V2** desenvolvido no Google. Isso é pré-treinado no conjunto de dados ImageNet, um grande conjunto de dados composto por 1,4 milhões de imagens e 1000 classes. O ImageNet é um conjunto de dados de treinamento de pesquisa com uma ampla variedade de categorias, como `jaca` e `seringa`. Essa base de conhecimento nos ajudará a classificar cães e gatos de nosso conjunto de dados específico.\n",
        "\n",
        "Primeiro, você precisa escolher qual camada do MobileNet V2 usará para extração de características. A última camada de classificação (na parte superior, como a maioria dos diagramas dos modelos de aprendizado de máquina vai de baixo para cima) não é muito útil. Em vez disso, você seguirá a prática comum de depender da última camada antes da operação de nivelamento. Essa camada é chamada de \"camada de gargalo\". Os recursos da camada de gargalo retêm mais generalidade em comparação com a camada final/superior.\n",
        "\n",
        "Primeiro, instancie um modelo MobileNet V2 pré-carregado com pesos treinados no ImageNet. Ao especificar o argumento **include_top = False**, você carrega uma rede que não inclui as camadas de classificação na parte superior, o que é ideal para a extração de características."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "19IQ2gqneqmS"
      },
      "outputs": [],
      "source": [
        "IMG_SHAPE = (IMG_SIZE, IMG_SIZE, 3)\n",
        "\n",
        "# Criar o modelo base a partir do modelo MobileNet V2 pré-treinado\n",
        "base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,\n",
        "                                               include_top=False,\n",
        "                                               weights='imagenet')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AqcsxoJIEVXZ"
      },
      "source": [
        "Este extrator de características converte cada imagem `160x160x3` em um bloco de características `5x5x1280`. Veja o que ele faz com o lote de imagens de exemplo:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Y-2LJL0EEUcx"
      },
      "outputs": [],
      "source": [
        "feature_batch = base_model(image_batch)\n",
        "print(feature_batch.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rlx56nQtfe8Y"
      },
      "source": [
        "## Extração de Características\n",
        "Nesta etapa, você congelará a base convolucional criada a partir da etapa anterior e utilizará como extrator de características. Além disso, você adiciona um classificador sobre ele e treina o classificador de nível superior."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CnMLieHBCwil"
      },
      "source": [
        "### Congelar a base convolucional\n",
        "\n",
        "É importante congelar a base convolucional antes de compilar e treinar o modelo. O congelamento (configurando layer.trainable = False) impede que os pesos em uma determinada camada sejam atualizados durante o treinamento. O MobileNet V2 possui muitas camadas, portanto, definir o sinalizador treinável do modelo inteiro como False congelará todas as camadas."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OTCJH4bphOeo"
      },
      "outputs": [],
      "source": [
        "base_model.trainable = False"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "KpbzSmPkDa-N"
      },
      "outputs": [],
      "source": [
        "# Vamos dar uma olhada na arquitetura do modelo base\n",
        "base_model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wdMRM8YModbk"
      },
      "source": [
        "### Adicionar um cabeçalho de classificação"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QBc31c4tMOdH"
      },
      "source": [
        "Para gerar previsões a partir do bloco de características, calcule a média dos espaços `5x5`, usando uma camada `tf.keras.layers.GlobalAveragePooling2D` para converter as características em um único vetor de 1280 elementos por imagem."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "dLnpMF5KOALm"
      },
      "outputs": [],
      "source": [
        "global_average_layer = tf.keras.layers.GlobalAveragePooling2D()\n",
        "feature_batch_average = global_average_layer(feature_batch)\n",
        "print(feature_batch_average.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "O1p0OJBR6dOT"
      },
      "source": [
        "Aplique uma camada `tf.keras.layers.Dense` para converter esses recursos em uma única previsão por imagem. Você não precisa de uma função de ativação aqui porque esta previsão será tratada como um `logit` ou um valor bruto de previsão. Números positivos predizem a classe 1, números negativos predizem a classe 0."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Wv4afXKj6cVa"
      },
      "outputs": [],
      "source": [
        "prediction_layer = keras.layers.Dense(1)\n",
        "prediction_batch = prediction_layer(feature_batch_average)\n",
        "print(prediction_batch.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0iqnBeZrfoIc"
      },
      "source": [
        "Agora empilhe o extrator de características e essas duas camadas usando um modelo `tf.keras.Sequential`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "eApvroIyn1K0"
      },
      "outputs": [],
      "source": [
        "model = tf.keras.Sequential([\n",
        "  base_model,\n",
        "  global_average_layer,\n",
        "  prediction_layer\n",
        "])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "g0ylJXE_kRLi"
      },
      "source": [
        "### Compilar o modelo\n",
        "\n",
        "Você deve compilar o modelo antes de treiná-lo. Como existem duas classes, use uma perda de entropia cruzada binária com `from_logits = True`, pois o modelo fornece uma saída linear."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "RpR8HdyMhukJ"
      },
      "outputs": [],
      "source": [
        "base_learning_rate = 0.0001\n",
        "model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=base_learning_rate),\n",
        "              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "I8ARiyMFsgbH"
      },
      "outputs": [],
      "source": [
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lxOcmVr0ydFZ"
      },
      "source": [
        "Os parâmetros de 2,5 milhões no MobileNet estão congelados, mas existem 1,2 mil parâmetros _trainable_ na camada Dense. Estes são divididos entre dois objetos `tf.Variable`, os pesos e desvios."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "krvBumovycVA"
      },
      "outputs": [],
      "source": [
        "len(model.trainable_variables)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RxvgOYTDSWTx"
      },
      "source": [
        "### Trainar o modelo\n",
        "\n",
        "Após o treinamento por 10 épocas, você deverá ver ~96% de acurácia.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "hlHEavK7DUI7"
      },
      "outputs": [],
      "source": [
        "num_train, num_val, num_test = (\n",
        "  metadata.splits['train'].num_examples*weight/10\n",
        "  for weight in SPLIT_WEIGHTS\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Om4O3EESkab1"
      },
      "outputs": [],
      "source": [
        "initial_epochs = 10\n",
        "steps_per_epoch = round(num_train)//BATCH_SIZE\n",
        "validation_steps=20\n",
        "\n",
        "loss0,accuracy0 = model.evaluate(validation_batches, steps = validation_steps)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8cYT1c48CuSd"
      },
      "outputs": [],
      "source": [
        "print(\"initial loss: {:.2f}\".format(loss0))\n",
        "print(\"initial accuracy: {:.2f}\".format(accuracy0))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JsaRFlZ9B6WK"
      },
      "outputs": [],
      "source": [
        "history = model.fit(train_batches,\n",
        "                    epochs=initial_epochs,\n",
        "                    validation_data=validation_batches)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Hd94CKImf8vi"
      },
      "source": [
        "### Curvas de Aprendizado\n",
        "\n",
        "Vamos dar uma olhada nas curvas de aprendizado da acurácia/perda do treinamento e da validação ao usar o modelo base do MobileNet V2 como um extrator de características fixo."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "53OTCh3jnbwV"
      },
      "outputs": [],
      "source": [
        "acc = history.history['accuracy']\n",
        "val_acc = history.history['val_accuracy']\n",
        "\n",
        "loss = history.history['loss']\n",
        "val_loss = history.history['val_loss']\n",
        "\n",
        "plt.figure(figsize=(8, 8))\n",
        "plt.subplot(2, 1, 1)\n",
        "plt.plot(acc, label='Training Accuracy')\n",
        "plt.plot(val_acc, label='Validation Accuracy')\n",
        "plt.legend(loc='lower right')\n",
        "plt.ylabel('Accuracy')\n",
        "plt.ylim([min(plt.ylim()),1])\n",
        "plt.title('Training and Validation Accuracy')\n",
        "\n",
        "plt.subplot(2, 1, 2)\n",
        "plt.plot(loss, label='Training Loss')\n",
        "plt.plot(val_loss, label='Validation Loss')\n",
        "plt.legend(loc='upper right')\n",
        "plt.ylabel('Cross Entropy')\n",
        "plt.ylim([0,1.0])\n",
        "plt.title('Training and Validation Loss')\n",
        "plt.xlabel('epoch')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "foWMyyUHbc1j"
      },
      "source": [
        "Nota: Se você está se perguntando por que as métricas de validação são claramente melhores que as métricas de treinamento, o principal fator é que camadas como `tf.keras.layers.BatchNormalization` e `tf.keras.layers.Dropout` afetam a acurácia durante o treinamento. Eles são desativados ao calcular a perda de validação.\n",
        "\n",
        "Em menor grau, é também porque as métricas de treinamento relatam a média de uma época, enquanto as métricas de validação são avaliadas após a época, portanto, as métricas de validação veem um modelo que foi treinado um pouco mais."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CqwV-CRdS6Nv"
      },
      "source": [
        "## Ajuste Fino\n",
        "No experimento de extração de características, você treinava apenas algumas camadas sobre um modelo base do MobileNet V2. Os pesos da rede pré-treinada não foram atualizados durante o treinamento.\n",
        "\n",
        "Uma maneira de aumentar ainda mais o desempenho é treinar (ou \"ajustar\") os pesos das camadas superiores do modelo pré-treinado, juntamente com o treinamento do classificador adicionado. O processo de treinamento forçará os pesos a serem ajustados com mapas de características genéricas para recursos associados especificamente ao conjunto de dados.\n",
        "\n",
        "Nota: Isso só deve ser tentado depois de você treinar o classificador de nível superior com o modelo pré-treinado definido como não treinável. Se você adicionar um classificador inicializado aleatoriamente sobre um modelo pré-treinado e tentar treinar todas as camadas em conjunto, a magnitude das atualizações de gradiente será muito grande (devido aos pesos aleatórios do classificador) e seu modelo pré-treinado esquecerá o que aprendeu.\n",
        "\n",
        "Além disso, você deve tentar ajustar um pequeno número de camadas superiores em vez de todo o modelo MobileNet. Na maioria das redes convolucionais, quanto mais alta a camada, mais especializada ela é. As primeiras camadas aprendem recursos muito simples e genéricos que generalizam para quase todos os tipos de imagens. À medida que você aumenta, as características são cada vez mais específicas para o conjunto de dados no qual o modelo foi treinado. O objetivo do ajuste fino é adaptar essas características especializados para trabalhar com o novo conjunto de dados, em vez de substituir o aprendizado genérico."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CPXnzUK0QonF"
      },
      "source": [
        "### Descongele as camadas superiores do modelo\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rfxv_ifotQak"
      },
      "source": [
        "Tudo o que você precisa fazer é descongelar o `base_model` e definir as camadas inferiores para que não possam ser treinadas. Em seguida, recompile o modelo (necessário para que essas alterações entrem em vigor) e reinicie o treinamento."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4nzcagVitLQm"
      },
      "outputs": [],
      "source": [
        "base_model.trainable = True"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-4HgVAacRs5v"
      },
      "outputs": [],
      "source": [
        "# Vamos dar uma olhada para ver quantas camadas existem no modelo base\n",
        "print(\"Number of layers in the base model: \", len(base_model.layers))\n",
        "\n",
        "# Ajuste a partir desta camada em diante\n",
        "fine_tune_at = 100\n",
        "\n",
        "# Congele todas as camadas antes de `fine_tune_at`\n",
        "for layer in base_model.layers[:fine_tune_at]:\n",
        "  layer.trainable =  False"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4Uk1dgsxT0IS"
      },
      "source": [
        "### Compilar o modelo\n",
        "\n",
        "Compile o modelo usando uma taxa de aprendizado muito menor."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NtUnaz0WUDva"
      },
      "outputs": [],
      "source": [
        "model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),\n",
        "              optimizer = tf.keras.optimizers.RMSprop(lr=base_learning_rate/10),\n",
        "              metrics=['accuracy'])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WwBWy7J2kZvA"
      },
      "outputs": [],
      "source": [
        "model.summary()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "bNXelbMQtonr"
      },
      "outputs": [],
      "source": [
        "len(model.trainable_variables)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4G5O4jd6TuAG"
      },
      "source": [
        "### Continue treinando o modelo"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0foWUN-yDLo_"
      },
      "source": [
        "Se você treinou para convergência anteriormente, esta etapa melhorará sua acurácia em alguns pontos percentuais."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ECQLkAsFTlun"
      },
      "outputs": [],
      "source": [
        "fine_tune_epochs = 10\n",
        "total_epochs =  initial_epochs + fine_tune_epochs\n",
        "\n",
        "history_fine = model.fit(train_batches,\n",
        "                         epochs=total_epochs,\n",
        "                         initial_epoch =  history.epoch[-1],\n",
        "                         validation_data=validation_batches)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TfXEmsxQf6eP"
      },
      "source": [
        "Vamos dar uma olhada nas curvas de aprendizado da acurácia/perda do treinamento e da validação ao ajustar as últimas camadas do modelo base do MobileNet V2 e treinar o classificador sobre ele. A perda de validação é muito maior do que a perda de treinamento, portanto, você pode obter um overfitting.\n",
        "\n",
        "Você também pode obter um overfitting, pois o novo conjunto de treinamento é relativamente pequeno e semelhante aos conjuntos de dados originais do MobileNet V2.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DNtfNZKlInGT"
      },
      "source": [
        "Após o ajuste fino, o modelo atinge quase 98% de acurácia."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "PpA8PlpQKygw"
      },
      "outputs": [],
      "source": [
        "acc += history_fine.history['accuracy']\n",
        "val_acc += history_fine.history['val_accuracy']\n",
        "\n",
        "loss += history_fine.history['loss']\n",
        "val_loss += history_fine.history['val_loss']"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "chW103JUItdk"
      },
      "outputs": [],
      "source": [
        "plt.figure(figsize=(8, 8))\n",
        "plt.subplot(2, 1, 1)\n",
        "plt.plot(acc, label='Training Accuracy')\n",
        "plt.plot(val_acc, label='Validation Accuracy')\n",
        "plt.ylim([0.8, 1])\n",
        "plt.plot([initial_epochs-1,initial_epochs-1],\n",
        "          plt.ylim(), label='Start Fine Tuning')\n",
        "plt.legend(loc='lower right')\n",
        "plt.title('Training and Validation Accuracy')\n",
        "\n",
        "plt.subplot(2, 1, 2)\n",
        "plt.plot(loss, label='Training Loss')\n",
        "plt.plot(val_loss, label='Validation Loss')\n",
        "plt.ylim([0, 1.0])\n",
        "plt.plot([initial_epochs-1,initial_epochs-1],\n",
        "         plt.ylim(), label='Start Fine Tuning')\n",
        "plt.legend(loc='upper right')\n",
        "plt.title('Training and Validation Loss')\n",
        "plt.xlabel('epoch')\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "_TZTwG7nhm0C"
      },
      "source": [
        "## Resumo:\n",
        "\n",
        "* ** Usando um modelo pré-treinado para extração de características **: Ao trabalhar com um pequeno conjunto de dados, é uma prática comum tirar proveito das características aprendidas por um modelo treinado em um conjunto de dados maior no mesmo domínio. Isso é feito instanciando o modelo pré-treinado e adicionando um classificador totalmente conectado na parte superior. O modelo pré-treinado é \"congelado\" e apenas os pesos do classificador são atualizados durante o treinamento.\n",
        "Nesse caso, a base convolucional extraiu todas as características associadas a cada imagem e você acabou de treinar um classificador que determina a classe da imagem, considerando esse conjunto de características extraídas.\n",
        "\n",
        "* ** Ajuste fino de um modelo pré-treinado **: Para melhorar ainda mais o desempenho, é possível redirecionar as camadas de nível superior dos modelos pré-treinados para o novo conjunto de dados via ajuste fino.\n",
        "Nesse caso, você ajustou seus pesos para que seu modelo aprendesse características de alto nível específicas ao conjunto de dados. Essa técnica geralmente é recomendada quando o conjunto de dados de treinamento é grande e muito semelhante ao conjunto de dados original em que o modelo pré-treinado foi treinado."
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [],
      "name": "transfer_learning.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
